<?php


namespace App\Helpers;

use App\Helpers\Redis\RedisHelper;
use Exception;

/**
 * 框架加载完成后：应用的一些核心全局方法提供
 *
 * Class MvcHelper
 *
 * @package App\Helpers
 * @author  kingsg
 */
class MvcHelper extends Helper
{
    private static string $token       = ''; // 用户token:生成的token: 热乎着呢，刚出锅的
    private static int    $tokenExpire = 0;  // token过期时间
    private static string $loginToken  = ''; // 已登录的用户token: 冷冷的冰雨在我身上胡乱地拍

    // 系统参数
    private static array $sysKeys = [
        'sys',    // 系统
        'timestamp',    // 时间戳[加密用]
        'version',    // 版本
        'token',    // 登录token
        'sign',    // 签名
        'device',  // 设备信息
        'version-code',  // 版本号： 对比用
    ];

    private static array $sysParams = [];    //系统参数
    private static array $params    = [];    //业务参数
    private static int   $userId    = 0;     //用户ID

    private static array $userInfo = []; // 当前登录的用户缓存

    /**
     * 加工请求参数
     *
     * @return array
     */
    public static function makeRequestParams(): array
    {
        $requestParams = [
            'sysParams' => static::getRequestSysParams(),
            'params' => static::getApiRequestParams(),
            'userId' => static::getLoginUserId(),
        ];

        // 敏感数据不保留到日志!!!
        if (isset($requestParams['params']['oldPayPwd'])) {
            $requestParams['params']['oldPayPwd'] = '';    // 旧密码
        }
        if (isset($requestParams['params']['payPwd'])) {
            $requestParams['params']['payPwd'] = '';    // 新密码
        }
        if (isset($requestParams['params']['confirmPayPwd'])) {
            $requestParams['params']['confirmPayPwd'] = '';    // 新密码确认
        }
        if (isset($requestParams['params']['identityCard'])) {
            $requestParams['params']['identityCard'] = '';    // 身份证号
        }

        return $requestParams;
    }

    /**
     * 获取API系统参数
     *
     * @return array
     */
    public static function getRequestSysParams(): array
    {
        if (!static::$sysParams) {
            $sysKeys = static::$sysKeys;
            foreach ($sysKeys as $item) {
                static::$sysParams[$item] = app()->request->header($item);
            }
        }

        /**
         * @tips
         * 默认为设置了代理
         * proxy_set_header X-Real-IP $remote_addr;
         * nginx配置的名称修改的话，这块也需要修改
         * 如果配置了代理，通过这个字段获取不到，则默认获取代理服务器的ip
         */
        static::$sysParams['ip'] = app()->request->header('X-Real-IP') ?? app()->request->ip();
        static::$sysParams['port'] = app()->request->server('REMOTE_PORT') ?: 0;

        return static::$sysParams;
    }

    /**
     * 获取API业务参数
     *
     * @return array
     */
    public static function getApiRequestParams(): array
    {
        if (!static::$params) {
            if (app()->request->header('Content-Type') === 'application/xml') {
                // 获取原始的 XML 数据
                $xmlData = app()->request->getContent();
                LogHelper::info('callback', ['params-xml' => $xmlData]);
                static::$params = xmlToArray($xmlData);
            } else {
                static::$params = (array)app()->request->all();
            }
        }

        return static::$params;
    }

    /**
     * 获取登录用户的userId
     *
     * @tips 此方法只有登录态的逻辑里调用才会取到用户ID!!!
     *
     * @return int
     */
    public static function getLoginUserId(): int
    {
        if (!static::$userId) {
            static::$userId = static::$userInfo['id'] ?? 0;
//            static::$userId = (int)(app()->request->input('userId') ?? 0);
        }

        return static::$userId;
    }

    /**
     * 设置sys
     *
     * @param string $sys
     */
    public static function setRequestSys(string $sys)
    {
        static::$sysParams['sys'] = $sys;
    }

    /**
     * 获取用户缓存
     *
     * @return array
     */
    public static function getUserInfoCache(): array
    {
        return static::$userInfo;
    }

    /**
     * 设置用户缓存
     *
     * @param array $userInfo
     */
    public static function setUserInfCache(array $userInfo): void
    {
        static::$userInfo = $userInfo;
    }

    /**
     * 获取服务的全局唯一ID
     *
     * @tips 后续再做重构
     * @throws Exception
     */
    public static function getUniqId(): string
    {
        // 从缓存获取
        $uniqId = RedisHelper::pop(RedisHelper::LIST_UNIQUE_ID);
        if ($uniqId === null) {
            // 缓存获取失败，实时生成，此处发送一条钉钉报警（表示预生成的速度小于使用的速度）
//            WatchDogHelper::sendText(WatchDogHelper::makeMsg('uniqId', '从缓存获取：服务的全局唯一ID失败，预生成的速度小于使用的速度'));
            return static::getBatchUniqId(1, true)[0];
        } else {
            return $uniqId;
        }
    }

    /**
     * 获取登录token
     *
     * @return string
     */
    public static function getLoginToken(): string
    {
        if (!static::$loginToken) {
            static::$loginToken = (string)(app()->request->header('X-Token') ?? '');
        }
        return static::$loginToken;
    }

    /**
     * 设置登录标识缓存
     */
    public static function setLoginCache(int $userId)
    {
        static::makeToken($userId);

        static::setLoginTokenCache(static::$token, $userId);
    }

    /**
     * 加工用户token
     *
     * @param $userId
     */
    public static function makeToken($userId)
    {
        // MD5(用户ID + 服务名称 + 服务秘钥 + 随机数 +  当前 Unix 时间戳和微秒数)
        if (!static::$token) {
            static::$token = static::makeMD5UniqId($userId);
        }
    }

    /**
     * 加工MD5唯一值
     *
     * @param int $userId
     * @param string $flag
     * @return string
     */
    public static function makeMD5UniqId(int $userId, string $flag = ''): string
    {
        return md5($userId . env('APP_NAME') . static::getAppKey() . mt_rand() . microtime() . $flag);
    }

    /**
     * 获取appKey
     *
     * @return string
     */
    public static function getAppKey(): string
    {
        $sys = static::$sysParams['sys'];
        switch ($sys) {
            case USER_REGISTER_SOURCE_APP_KEY:
                return env('APP_KEY');
            default:
                return '';
        }
    }

    /**
     * 设置登录token缓存
     *
     * @param int $userId
     * @param string $token
     */
    public static function setLoginTokenCache(string $token, int $userId)
    {
        // 1. 清除旧的登录缓存【1.主动清除垃圾缓存数据，2.避免出现多端登录】
        MvcHelper::clearLoginCache(MvcHelper::getLoginTokenByUserId($userId), $userId);

        // 2. 登录缓存的过期时间
        $expired = static::getLoginCacheExpired();

        // 3. 用户ID为值
        $userIdCacheKey = static::getLoginUserIdCacheKey($token);
        RedisHelper::set($userIdCacheKey, $userId, $expired);

        // 4. token为值： 可以用来指定用户ID找token
        $tokenCacheKey = static::getLoginTokenCacheKey($userId);
        RedisHelper::set($tokenCacheKey, $token, $expired + 10 * ONE_MINUTE);
    }

    /**
     * 清除登录标识缓存
     */
    public static function clearLoginCache(string $token, int $userId)
    {
        $userIdCacheKey = static::getLoginUserIdCacheKey($token);
        $tokenCacheKey = static::getLoginTokenCacheKey($userId);

        RedisHelper::del($userIdCacheKey, $tokenCacheKey);
    }

    /**
     * 获取登录用户ID缓存key
     *
     * @param string $token
     * @return string
     */
    public static function getLoginUserIdCacheKey(string $token): string
    {
        // 16位的登录token盐值：如果要全员踢下线可以更改配置达到目的
        $salt = env('APP_LOGIN_SALT', 'CA743315F3503F');

        return RedisHelper::makeKey(RedisHelper::STRING_LOGIN_USERID, static::$sysParams['sys'], $salt, $token);
    }

    /**
     * 获取登录token缓存key
     *
     * @param string $userId
     * @return string
     */
    public static function getLoginTokenCacheKey(string $userId): string
    {
        // 16位的登录token盐值：如果要全员踢下线可以更改配置达到目的
        $salt = env('APP_LOGIN_SALT', 'F1A0DCE908200A2D');

        return RedisHelper::makeKey(RedisHelper::STRING_LOGIN_TOKEN, static::$sysParams['sys'], $salt, $userId);
    }

    /**
     * 通过用户ID获取token
     *
     * @param int $userId
     * @return mixed
     */
    public static function getLoginTokenByUserId(int $userId)
    {
        $tokenCacheKey = static::getLoginTokenCacheKey($userId);

        return RedisHelper::get($tokenCacheKey) ?: '';
    }

    /**
     * 获取登录缓存过期时间
     *
     * @return float|int
     */
    private static function getLoginCacheExpired()
    {
        // 过期时间： 7天 + 100~999 分钟
        return env('LOGIN_REDIS_CACHE_DAY', 7) * SECONDS_DAY + ONE_MINUTE * rand(100, 999);
    }

    /**
     * 通过token获取用户ID
     *
     * @param string $token
     * @return mixed
     */
    public static function getLoginUserIdByToken(string $token)
    {
        $userIdCacheKey = static::getLoginUserIdCacheKey($token);

        return RedisHelper::get($userIdCacheKey) ?: 0;
    }

    /**
     * 通过token获取登录态的剩余时间
     *
     * @param string $token
     * @return mixed
     */
    public static function getLoginTtlByToken(string $token)
    {
        $userIdCacheKey = static::getLoginUserIdCacheKey($token);

        return RedisHelper::TTL($userIdCacheKey);
    }

    /**
     * 设置token过期时间
     *
     * @param int $expireAt
     * @return void
     */
    public static function makeLoginTokenExpire(int $expireAt): void
    {
        self::$tokenExpire = $expireAt;
    }

    /**
     * 获取token过期时间
     *
     * @return int
     */
    public static function getLoginTokenExpire(): int
    {
        return self::$tokenExpire;
    }

    /**
     * 获取API前缀
     *
     * @return string
     */
    public static function getBaseApi(): string
    {
        return env("BASE_API", "");
    }
}
